/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bean.session;

import entity.CalendarEntity;
import entity.MasterProductionScheduleDailyEntity;
import entity.MasterProductionScheduleMonthlyEntity;
import entity.ProductPercentageQuantityEntity;
import java.util.ArrayList;
import java.util.Date;
import java.util.Vector;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Root;
import type.Month;
import type.ProductionType;
import type.SalesForecastPeriodType;

/**
 *
 * @author wei
 */
@Stateless
public class MasterProductionScheduleDailyEntityFacade extends AbstractFacade<MasterProductionScheduleDailyEntity> {

    @PersistenceContext(unitName = "MacTeam-warPU")
    private EntityManager em;
    @EJB
    private bean.session.MasterProductionScheduleWeeklyEntityFacade weeklyFacade;

    protected EntityManager getEntityManager() {
        return em;
    }

    public MasterProductionScheduleDailyEntityFacade() {
        super(MasterProductionScheduleDailyEntity.class);
    }

    public void createDaily(MasterProductionScheduleMonthlyEntity monthlyMPS) {
        int day = 1;
        for (int j = 0; j < monthlyMPS.getMonthlyWorkingDays(); j++) {
            MasterProductionScheduleDailyEntity dailyMPS = new MasterProductionScheduleDailyEntity();
            //set day
            day = getDay(day, monthlyMPS.getMonthOfProduction(), monthlyMPS.getYearOfProduction());
            dailyMPS.setDayofProduction(day);
            dailyMPS.setMpsMonthly(monthlyMPS);
            dailyMPS.setYearOfProduction(monthlyMPS.getYearOfProduction());
            ArrayList<ProductPercentageQuantityEntity> monthlyPPQ = new ArrayList<ProductPercentageQuantityEntity>(monthlyMPS.getPlannedPPQ());
            ArrayList<ProductPercentageQuantityEntity> dailyPPQ = new ArrayList<ProductPercentageQuantityEntity>();
            for (int i = 0; i < monthlyMPS.getPlannedPPQ().size(); i++) {
                ProductPercentageQuantityEntity ppq = new ProductPercentageQuantityEntity();
                ppq.setProduct(monthlyPPQ.get(i).getProduct());
                ppq.setPercentage(monthlyPPQ.get(i).getPercentage());
                //ppq.setQuantity((long) Math.ceil((1.0 * monthlyPPQ.get(i).getQuantity())/monthlyMPS.getMonthlyWorkingDays()));
                ppq.setQuantity(monthlyPPQ.get(i).getQuantity() / monthlyMPS.getMonthlyWorkingDays());
                
                if (ppq.getProduct().getProductName().equals("Classic Nutri Bar (Nuts)")) {
                    dailyMPS.setClassicNuts(ppq.getQuantity());
                } else if (ppq.getProduct().getProductName().equals("Classic Nutri Bar (Fruits)")) {
                    dailyMPS.setClassicFruits(ppq.getQuantity());
                } else if (ppq.getProduct().getProductName().equals("Premium Power Bar (Chocolate Nuts)")) {
                    dailyMPS.setPremiumNuts(ppq.getQuantity());
                } else if (ppq.getProduct().getProductName().equals("Premium Power Bar (Redcurrant Fruits)")) {
                    dailyMPS.setPremiumFruits(ppq.getQuantity());
                }
                dailyPPQ.add(ppq);
            }
            dailyMPS.setBreakdownByProduct(dailyPPQ);
            dailyMPS.setProductionType(monthlyMPS.getProductionType());
            dailyMPS.setChangeOverTime(calculateDailyChangeOverTime(dailyMPS));
            dailyMPS.setCapacityNormal(8);
            dailyMPS.setCapacityOverTime(4);
            calculateUtil(dailyMPS);
            dailyMPS.setCreatedBy(monthlyMPS.getCreatedBy());
            monthlyMPS.getMpsDaily().add(dailyMPS);
            //create(dailyMPS);
            day++;
        }
    }

    public void createWeekly(MasterProductionScheduleMonthlyEntity monthlyMPS) {
        weeklyFacade.createWeekly(new ArrayList<MasterProductionScheduleDailyEntity>(monthlyMPS.getMpsDaily()));
    }

    public void updateDaily(MasterProductionScheduleMonthlyEntity monthlyMPS) {
        int day = 1;
        ArrayList<ProductPercentageQuantityEntity> monthlyPPQ = new ArrayList<ProductPercentageQuantityEntity>(monthlyMPS.getPlannedPPQ());
        ArrayList<MasterProductionScheduleDailyEntity> dailyMPS = new ArrayList<MasterProductionScheduleDailyEntity>(monthlyMPS.getMpsDaily());
        for (int i = 0; i < dailyMPS.size(); i++) {
            ArrayList<ProductPercentageQuantityEntity> dailyPPQ = new ArrayList<ProductPercentageQuantityEntity>(dailyMPS.get(i).getBreakdownByProduct());
            for (int k = 0; k < dailyPPQ.size(); k++) {
                dailyPPQ.get(k).setPercentage(monthlyPPQ.get(k).getPercentage());
                dailyPPQ.get(k).setQuantity(monthlyPPQ.get(k).getQuantity() / monthlyMPS.getMonthlyWorkingDays());
            }
        }
    }

    public int getDay(int day, Month month, int year) {
        day = checkWeekends(day, month, year);
        ArrayList<Integer> publicHoliday = getPublicHolidaysThisMonth(month, year);
        for (int i = 0; i < publicHoliday.size(); i++) {
            if (day == publicHoliday.get(i)) {
                day++;
                for (int j = i + 1; j < publicHoliday.size() - i; j++) {
                    if (day == publicHoliday.get(j)) {
                        day++;
                    }
                }
            }
        }
        return day;
    }

    public int checkWeekends(int day, Month month, int year) {
        Date date = new Date(year - 1900, month.getMonthNum() - 1, day);
        if (date.getDay() == 0 || date.getDay() == 6) {
            day++;
            Date checksunday = new Date(year - 1900, month.getMonthNum() - 1, day);
            if (checksunday.getDay() == 0 || checksunday.getDay() == 6) {
                day++;
            }
        }
        return day;
    }

    public ArrayList<Integer> getPublicHolidaysThisMonth(Month month, int year) {
        CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery cq = builder.createQuery();
        Root<CalendarEntity> calendarRoot = cq.from(CalendarEntity.class);
        cq.select(calendarRoot);
        cq.where(builder.and(builder.equal(calendarRoot.get("archived"), false), builder.equal(calendarRoot.get("month"), month), builder.equal(calendarRoot.get("year"), year)));

        ArrayList day = new ArrayList<Integer>();
        for (Object o : em.createQuery(cq).getResultList()) {
            CalendarEntity c = (CalendarEntity) o;
            day.add(c.getDay());
        }
        return day;
    }

    public void editAll(ArrayList<MasterProductionScheduleDailyEntity> dailyMPS) {
        for (int i = 0; i < dailyMPS.size(); i++) {
            edit(dailyMPS.get(i));
        }
    }

    public double calculateDailyChangeOverTime(MasterProductionScheduleDailyEntity dailyMPS) {
        //find total number of different types of products producing on that day
        //find total number of hours required to produce all products
        int types = 0;
        double hours = 0;
        double changeOverTime = 0;
        ArrayList<ProductPercentageQuantityEntity> dailyPPQ = new ArrayList<ProductPercentageQuantityEntity>(dailyMPS.getBreakdownByProduct());
        for (int i = 0; i < dailyPPQ.size(); i++) {
            if (dailyPPQ.get(i).getQuantity() != 0) {
                types++;
            }
            hours = hours + ((1.0 / dailyPPQ.get(i).getProduct().getCasesOfHour()) * dailyPPQ.get(i).getQuantity());
        }
        //System.out.println("types=" + types);
        //if product 0 or 1 type of product a day, changeOverTime = 0;
        if (types == 0 || types == 1) {
            return changeOverTime;
        } else {
            //check total time to produce, including changeOverTime, will it exceed normal working hours?
            changeOverTime = (types - 1) * 0.5;
            double totalTime = hours + changeOverTime;
            //System.out.print("totalTime=" + totalTime + "hours=" + hours + "changeOverTime=" + changeOverTime);
            //if it doesn't, changeOverTime = types * 0.5;
            if (totalTime < 8) {
                return changeOverTime;
            } else {
                //means there will be ot
                //check if changeOverTime can be reduced through end-of-shift clean up
                double overtimeHours = totalTime - 8;
                //System.out.println("overTimeHours=" + overtimeHours);
                //if the overTimeHours = exactly the time to produce one of the products, changeOverTime - 1;
                if (types >= 2) {
                    for (int i = 0; i < dailyPPQ.size(); i++) {
                        double hoursPerProduct = (1.0 / dailyPPQ.get(i).getProduct().getCasesOfHour()) * dailyPPQ.get(i).getQuantity();
                        if (hoursPerProduct == overtimeHours) {
                            changeOverTime = changeOverTime - 1;
                        }
                    }
                }
                if (types >= 3) {
                    double hoursPerProduct = 0;
                    for (int i = 0; i < dailyPPQ.size(); i++) {
                        for (int j = i + 1; j < dailyPPQ.size(); j++) {
                            hoursPerProduct = 0.5 + ((1.0 / dailyPPQ.get(i).getProduct().getCasesOfHour()) * dailyPPQ.get(i).getQuantity()) + ((1.0 / dailyPPQ.get(j).getProduct().getCasesOfHour()) * dailyPPQ.get(j).getQuantity());
                            if (hoursPerProduct == overtimeHours) {
                                changeOverTime = changeOverTime - 1;
                            }
                        }
                    }
                }
                if (types == 4) {
                    double hoursPerProduct = 0;
                    for (int i = 0; i < dailyPPQ.size(); i++) {
                        for (int j = i + 1; j < dailyPPQ.size(); j++) {
                            for (int k = i + 2; k < dailyPPQ.size(); k++) {
                                hoursPerProduct = 1 + ((1.0 / dailyPPQ.get(i).getProduct().getCasesOfHour()) * dailyPPQ.get(i).getQuantity()) + ((1.0 / dailyPPQ.get(j).getProduct().getCasesOfHour()) * dailyPPQ.get(j).getQuantity()) + ((1.0 / dailyPPQ.get(k).getProduct().getCasesOfHour()) * dailyPPQ.get(k).getQuantity());
                            }
                            if (hoursPerProduct == overtimeHours) {
                                changeOverTime = changeOverTime - 1;
                            }
                        }
                    }
                }
            }
        }
        return changeOverTime;
    }

    public double calculateMonthlyChangeOverTime(MasterProductionScheduleMonthlyEntity monthlyMPS) {
        double changeOverTime = 0;
        ArrayList<MasterProductionScheduleDailyEntity> dailyMPS = new ArrayList<MasterProductionScheduleDailyEntity>(monthlyMPS.getMpsDaily());
        for (int i = 0; i < dailyMPS.size(); i++) {
            changeOverTime = changeOverTime + calculateDailyChangeOverTime(dailyMPS.get(i));
        }
        return changeOverTime;
    }

    public DataModel getAllDailyMps() {
        Query query = em.createQuery("SELECT m FROM MasterProductionScheduleDaily m WHERE m.archived!= 'true'");
        return new ListDataModel(query.getResultList());
    }

    public DataModel listCreateMPS(SalesForecastPeriodType period, int year, Month month, ProductionType type) {
        CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery cq = builder.createQuery();
        Root<MasterProductionScheduleMonthlyEntity> mpsRoot = cq.from(MasterProductionScheduleMonthlyEntity.class);
        cq.select(mpsRoot);
        cq.where(builder.and(builder.equal(mpsRoot.get("archived"), false), builder.equal(mpsRoot.get("type"), period), builder.equal(mpsRoot.get("yearOfProduction"), year), builder.equal(mpsRoot.get("monthOfProduction"), month), builder.equal(mpsRoot.get("productionType"), type)));

        return new ListDataModel(em.createQuery(cq).getResultList());
    }

    public long getPreviousMonthOnHand(MasterProductionScheduleMonthlyEntity mps) {
        int year = mps.getYearOfProduction();
        Month month = mps.getMonthOfProduction();

        if (mps.getMonthOfProduction().equals(Month.JANUARY)) {
            month = Month.DECEMBER;
            year = year - 1;
        } else {
            month = Month.fromMonthNum(month.getMonthNum() - 1);
        }

        CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery cq = builder.createQuery();
        Root<MasterProductionScheduleMonthlyEntity> mpsRoot = cq.from(MasterProductionScheduleMonthlyEntity.class);
        cq.select(mpsRoot);
        cq.where(builder.and(builder.equal(mpsRoot.get("archived"), false), builder.equal(mpsRoot.get("monthOfProduction"), month), builder.equal(mpsRoot.get("yearOfProduction"), year)));

        MasterProductionScheduleMonthlyEntity m = (MasterProductionScheduleMonthlyEntity) em.createQuery(cq).getResultList().get(0);
        return m.getSafetyStock();
    }
    
    public DataModel getAllDailyMPSThisMonth(MasterProductionScheduleDailyEntity dailyMPS) {
        Query query = em.createQuery("SELECT d FROM MasterProductionScheduleDaily d INNER JOIN d.mpsMonthly m WHERE d.archived!= 'true' AND m.monthOfProduction = :month AND d.yearOfProduction = " + dailyMPS.getYearOfProduction() + " AND d.productionType = :productionType");
        query.setParameter("month", dailyMPS.getMpsMonthly().getMonthOfProduction());
        query.setParameter("productionType", dailyMPS.getProductionType());
        
        return new ListDataModel(query.getResultList());
    }
    
    public long calculateAllQuantity(MasterProductionScheduleDailyEntity dailyMPS) {
        long allQuantity = 0;
        
        //Vector<MasterProductionScheduleDailyEntity> mps = (Vector<MasterProductionScheduleDailyEntity>) getAllDailyMPSThisMonth(dailyMPS).getWrappedData();
        MasterProductionScheduleMonthlyEntity monthlyMps = dailyMPS.getMpsMonthly();
        ArrayList<MasterProductionScheduleDailyEntity> mps = new ArrayList<MasterProductionScheduleDailyEntity>(monthlyMps.getMpsDaily());
        
        for (int j = 0; j < mps.size(); j++) {
            MasterProductionScheduleDailyEntity m = mps.get(j);
            ArrayList<ProductPercentageQuantityEntity> dailyPPQ = new ArrayList<ProductPercentageQuantityEntity>(m.getBreakdownByProduct());
            for (int i = 0; i < dailyPPQ.size(); i++) {
                allQuantity = allQuantity + dailyPPQ.get(i).getQuantity();
            }
        }
        return allQuantity;
    }
    
    public long calculateEachProduct(MasterProductionScheduleDailyEntity dailyMPS, String productName) {
        long allQuantity = 0;
        
        //Vector<MasterProductionScheduleDailyEntity> mps = (Vector<MasterProductionScheduleDailyEntity>) getAllDailyMPSThisMonth(dailyMPS).getWrappedData();
        MasterProductionScheduleMonthlyEntity monthlyMps = dailyMPS.getMpsMonthly();
        ArrayList<MasterProductionScheduleDailyEntity> mps = new ArrayList<MasterProductionScheduleDailyEntity>(monthlyMps.getMpsDaily());
        
        for (int j = 0; j < mps.size(); j++) {
            MasterProductionScheduleDailyEntity m = mps.get(j);
            ArrayList<ProductPercentageQuantityEntity> dailyPPQ = new ArrayList<ProductPercentageQuantityEntity>(m.getBreakdownByProduct());
            for (int i = 0; i < dailyPPQ.size(); i++) {
                if (dailyPPQ.get(i).getProduct().getProductName().equals(productName))
                    allQuantity = allQuantity + dailyPPQ.get(i).getQuantity();
            }
        }
        return allQuantity;
    }
    
    public ArrayList<MasterProductionScheduleMonthlyEntity> getPeriodMPS(MasterProductionScheduleMonthlyEntity monthlyMPS) {
        CriteriaBuilder builder = getEntityManager().getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery cq = builder.createQuery();
        Root<MasterProductionScheduleMonthlyEntity> mpsRoot = cq.from(MasterProductionScheduleMonthlyEntity.class);
        cq.select(mpsRoot);
        cq.where(builder.and(builder.equal(mpsRoot.get("archived"), false), builder.equal(mpsRoot.get("type"), monthlyMPS.getType()), builder.equal(mpsRoot.get("yearOfProduction"), monthlyMPS.getYearOfProduction()), builder.equal(mpsRoot.get("productionType"), monthlyMPS.getProductionType())));
        
        ArrayList<MasterProductionScheduleMonthlyEntity> periodMPS = new ArrayList<MasterProductionScheduleMonthlyEntity>();
        for (Object o : em.createQuery(cq).getResultList()) {
            MasterProductionScheduleMonthlyEntity m = (MasterProductionScheduleMonthlyEntity) o;
            periodMPS.add(m);
        }
        return periodMPS;
    }
    
    public void calculateUtil(MasterProductionScheduleDailyEntity dailyMPS) {
        System.out.println("calculating daily utilization");
        ArrayList<ProductPercentageQuantityEntity> ppq = new ArrayList<ProductPercentageQuantityEntity>(dailyMPS.getBreakdownByProduct());
        double util = 0;
        for (int i = 0; i < ppq.size(); i++) {
            long quantity = ppq.get(i).getQuantity();
            util = util + ((1.0 / ppq.get(i).getProduct().getCasesOfHour()) * quantity);
            System.out.println("quantity="+quantity+"util="+util);
        }

        util = util + dailyMPS.getChangeOverTime();
        double maxNormal = (dailyMPS.getCapacityNormal() * 0.95);
        double otQuantity = 0;
//        System.out.println("util:" + util + "changeOverTime:" + dailyMPS.getChangeOverTime());
//        System.out.println("maxNormal:" + maxNormal);
        if (util >= maxNormal) {
            otQuantity = Math.ceil(util - maxNormal);
            util = maxNormal;
            dailyMPS.setUtilizationNormal(Math.round((util / dailyMPS.getCapacityNormal()) * 1000) / 10);
            dailyMPS.setUtilizationOverTime(Math.round((otQuantity / dailyMPS.getCapacityNormal()) * 1000) / 10);
        } else {
            dailyMPS.setUtilizationNormal(Math.round((util / dailyMPS.getCapacityNormal()) * 1000) / 10);
            System.out.println(dailyMPS.getUtilizationNormal());
            dailyMPS.setUtilizationOverTime(Math.round((otQuantity / dailyMPS.getCapacityNormal()) * 1000) / 10);
        }
    }
}
